#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>

struct gpio_desc {
	unsigned int group;
	unsigned int num[16];
};

enum SRF05_IO{
	TRIG,
	ECHO,
	NONE
};

static struct gpio_desc gpio_val[6];

//初始化gpio管脚编号
static void gpio_init (void)
{
	int i, j;
	for (i = 0; i < 6; i++) {
		gpio_val[i].group = i;
		for (j = 0; j < 16; j++) {
			gpio_val[i].num[j] = 496 - 16*i + j;
		}
	}
}

//根据组号和组内编号获取数字
static unsigned int gpio_get (struct gpio_desc gpios)
{
	return gpio_val[gpios.group].num[gpios.num[0]];
}

//根据用户自己的需求配置节点文件下的属性文件
static int gpio_write (char *pptr, char *attr, char *val)
{
	int fd, len;
	char file_path[256];
	//file_path: /sys/class/gpio/gpio497/attr(direction/edge...)
	sprintf (file_path, "%s/%s", pptr, attr);
	if (0 > (fd = open (file_path, O_WRONLY)))
	{
		perror ("open file_path error!");
		return -1;
	}
	len = strlen (val);
	if (len != write (fd, val, len)) {
		perror ("write val error!");
		close (fd);
		return -1;
	}
	close (fd);
	return 0;
}

/*******************************************************
func: 将gpio管脚对应的设备文件导出到用户空间
	 gpio4_4 配置为输出模式, 默认输出低电平
	 gpio4_5 配置为输入模式, 默认用中断的模式接受信号
param: gpios, type
return: 成功0 错误 -1
*******************************************************/
static int SRF05_if_set (struct gpio_desc gpios, enum SRF05_IO type)
{
	char gpio_num[6];
	char file_path[128];
	int fd, len = 0;
	sprintf (gpio_num, "%d", gpio_get(gpios));
	sprintf (file_path, "/sys/class/gpio/gpio%s", gpio_num);

	if (access (file_path, F_OK)) {
		if (0 > (fd = open ("/sys/class/gpio/export", O_WRONLY))){
			perror ("open error!");
			return -1;
		}
		len = sizeof(gpio_num) / sizeof(char);
		if(len != write (fd, gpio_num, len)){
			perror ("write error!");
			close (fd);
			return -1;
		}
	}

	//trig: gpio4_4
	if (type == TRIG) {
		if (0 > (gpio_write (file_path, "edge", "none")))
		{
			printf ("config edge attr error!");
			return -1;
		}
		if (0 > (gpio_write (file_path, "direction", "out")))
		{
			printf ("config direction attr error!");
			return -1;
		}
		if (0 > (gpio_write (file_path, "value", "0")))
		{
			printf ("config value attr error!");
			return -1;
		}
	} else if (type == ECHO) {
		if (0 > (gpio_write (file_path, "edge", "both")))
		{
			printf ("config edge attr error!");
			return -1;
		}
		if (0 > (gpio_write (file_path, "direction", "in")))
		{
			printf ("config direction attr error!");
			return -1;
		}
	} 
	return 0;
}

static int SRF05_open_echo (struct gpio_desc gpios)
{
	char gpio_num[6];
	char pptr[128];
	char fp[256];
	int echo_fd;

	sprintf (gpio_num, "%d", gpio_get(gpios));
	memset (pptr, 0, sizeof(pptr)/sizeof(char));
	sprintf (pptr, "/sys/class/gpio/gpio%s", gpio_num);

	memset (fp, 0, sizeof(fp)/sizeof(char));
	sprintf (fp, "%s/%s", pptr, "value");
	echo_fd = open (fp, O_RDONLY);
	return echo_fd;
}

/****************************************
func: gpio4_4, trigger管脚产生一个10us的脉冲
param: gpios, 保存的组号和组内编号
return: 成功 0 错误 -1
****************************************/
static int SRF05_trigger (struct gpio_desc gpios)
{
	
	char gpio_num[6];
	char pptr[128];
	struct timespec ts, tc;

	sprintf (gpio_num, "%d", gpio_get(gpios));
	memset (pptr, 0, sizeof(pptr)/sizeof(char));
	sprintf (pptr, "/sys/class/gpio/gpio%s", gpio_num);

	if (gpio_write (pptr, "value", "1")) {
		perror ("config gpio value attr error!");
		return -1;
	}
	//10us
	clock_gettime (CLOCK_REALTIME, &ts);
	while ((tc.tv_nsec - ts.tv_nsec)/1000 <= 10) {
		for (int i = 0; i < 5; i++);
		clock_gettime (CLOCK_REALTIME, &tc);
	}

	if (gpio_write (pptr, "value", "0")) {
		perror ("config gpio value attr error!");
		return -1;
	}
	return 0;
}

/******************************************
func: 监听echo的valu属性文件，测量时间
	 并根据测得的时间, 计算并打印距离信息
param: echo_fd， echo节点下的属性文件value的文件描述符
return: void
******************************************/
static void SRF05_get_distance (int fd) 
{
	struct pollfd fds[1];
	char buf[128] = {0};
	int ret  = 0;
	struct timespec tc, tf;
	float distance;

	fds[0].fd = fd;
	fds[0].events = POLLPRI;
	if (-1 ==  (ret = read (fd, buf, 10))) {
		perror ("read error!");
		return ;
	}

	while (1) {
		ret = poll (fds, 1, -1);
		if (-1 == ret) {
			perror ("poll");
			return ;
		}
		if (fds[0].revents & POLLPRI) {
			ret = lseek (fd, 0, SEEK_SET);
			if (ret == -1) {
				perror ("lseek error");
			}
			memset (buf, 0, sizeof(buf)/sizeof(buf[0]));
			ret = read (fd, buf, 1);
			if (ret == -1) {
				perror ("read error");
			}
			if (atoi(buf) == 1) {
				clock_gettime (CLOCK_REALTIME, &tc);
			} else if (atoi(buf) == 0) {
				clock_gettime (CLOCK_REALTIME, &tf);
				distance = (tf.tv_nsec - tc.tv_nsec) / 1000 / 29.4 / 2;
				if (distance > 0) {
					printf ("distance: %.2fcm\n", distance);
				}
				break;
			}
		}
	}
}


int main (int argc, char** argv)
{
	struct gpio_desc gpios;
	int ret;
	int echo_fd;
	//初始化gpio管脚
	gpio_init ();

	//trigger: gpio4_4配置为输出模式
	gpios.group = 4;
	gpios.num[0] = 4;
	ret = SRF05_if_set (gpios, TRIG);
	if (ret < 0) {
		perror ("trigger gpio init error");
		exit (-1);
	}
	//echo: gpio4_5配置为输入模式
	gpios.group = 4;
	gpios.num[0] = 5;
	ret = SRF05_if_set (gpios, ECHO);
	if (ret < 0) {
		perror ("echo gpio init error");
		exit (-1);
	}
	//打开echo的文件描述符
	echo_fd = SRF05_open_echo (gpios);
	if (-1 == echo_fd) {
		perror ("open value attr error!");
		exit (-1);
	}
	while (1) {
		//trigger: send a signal
		gpios.group = 4;
		gpios.num[0] = 4;
		SRF05_trigger (gpios);

		SRF05_get_distance (echo_fd);
		usleep (100000);
	}
	return 0;
}
